# 1. 名字解释：MAC地址、IP地址、TCP、UDP
# MAC地址则是48位的（6个字节），通常表示为12个16进制数，每2个16进制数之间用冒号隔开，
# 如08：00：20：0A：8C：6D就是一个MAC地址。
# 其前3字节表示OUI（Organizationally Unique Identifier），
# 是IEEE的注册管理机构给不同厂家分配的代码，区分不同的厂家。后3字节由厂家自行分配
import selectors
import socket
import threading


# 2. 阐述如有一主机A向主机B发送数据包在网络上经过的各个步骤
# 同一网段主机A查找自身ARP表中B主机ARP表项，如果没有ARP表项则执行广播ARP查找流程，
# 根据ARP表项封装数据包MAC地址在本广播域中发送，B主机收到后匹配mac地址为本机接口mac地址后拆封至传输层通过端口匹配进程，
# 交由对应进程处理。
# 不同网段主机A需要查找网关的ARP表项，封装MAC转发至网关，具体过程与同网段的发送过程相同，网关收到IP包后根据路由表转发，
# 二层封装转发流程与同网段流程类似，后面描述省略，当IP包转发至目的主机B后，B解封数据帧到传输层，根据IP及协议端口号找到对应进程，
# B的对应进程处理A发来的实际数据
# 3. 利用本周学习的知识实现如下效果的程序：
#
# 1. server端监听指定的tcp端口
# 2. server端预先实现简单加减法的代码(可以自行扩展其他更复杂功能)
# 3. client端可以通过socket连接与server端通信传输需要参数
# 4. server端根据传的参数计算结果并返回
# 5. client端打印返回结果


class CalcServer:
    def __init__(self,ip='0.0.0.0',port=9999):
        self.addr = ip,port
        self.sock = socket.socket()
        self.sock.setblocking(False)
        self.event = threading.Event()
        self.selector = selectors.DefaultSelector()


    def start(self):
        self.sock.bind(self.addr)
        self.sock.listen()
        self.selector.register(self.sock,selectors.EVENT_READ,self.accept)
        threading.Thread(target=self.select,name='select').start()


    def stop(self):
        self.event.set()


    def select(self):
        with self.selector:
            while not self.event.is_set():
                events = self.selector.select()
                for key,mask in events:
                    key.data(key.fileobj,mask)


    def accept(self,server:socket.socket,mask):
        conn,raddr = server.accept()
        conn.setblocking(False)
        self.selector.register(conn,selectors.EVENT_READ,self.recv)


    def recv(self,conn:socket.socket,mask):
        try:
            data = conn.recv(1024).strip()
            if data == b'' or data == b'quit':
                self.selector.unregister(conn)
                conn.close()
                return
            data = self.calc(data).encode()
            conn.send(data)
        except ValueError:
            conn.send("The input content is incorrect".encode())


    def calc(self,data:bytes) -> str:
        if len(data.decode().split("+")) == 2:
            data = data.decode().split("+")
            return str(float(data[0]) + float(data[1]))
        elif len(data.decode().split("-")) == 2:
            data = data.decode().split("-")
            return str(float(data[0]) - float(data[1]))
        else:
            return "Do not support! EX 1+2 or 5-2"


if __name__ == '__main__':
    cs = CalcServer()
    cs.start()
    while True:
        cmd = input('二元加减运算>>').strip()
        if cmd == 'quit':
            cs.stop()
            break
        print(*cs.selector.get_map().values())



